package com.podmanager.search;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import com.podmanager.RssFeed.RSSParser;
import com.podmanager.UI.R;
import com.podmanager.file.FileUtility;

public class ImageLoader {
	MemoryCache memoryCache=new MemoryCache();
	FileCache fileCache;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 
    
    public ImageLoader(Context context){
        fileCache=new FileCache(context);
        executorService=Executors.newFixedThreadPool(5);
    }
    

	public void DisplayImage(SearchRecord sr) 
	{
		
        Bitmap bitmap=memoryCache.get(sr.getFeedUrl());
        ImageView imageView=sr.getImageView();
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);
        else
        {
            imageView.setImageResource(R.drawable.loading);
            executorService.submit(new RunnableImageLoader(sr));
        }
    }
            
    private Bitmap getBitmap(String url)
    {
    	try {
	        File f=fileCache.getFile(url);
	 
	        //from SD cache
	        Bitmap b = decodeFile(f);
	        if(b!=null)
	            return b;
	 
	        //from web
        
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            InputStream is=conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            FileUtility.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }
    
    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f),null,o);
 
            //Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE=100;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }
 
            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize=scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {}
        return null;
    }

    class RunnableImageLoader implements Runnable {
    	SearchRecord searchRecord;
        RunnableImageLoader(SearchRecord sr){
            this.searchRecord=sr;
        }
 
        @Override
        public void run() {
        	if(searchRecord.getImageURL()==null)
        		searchRecord.setImageURL(getImageUrlFromRssUrl(searchRecord.getFeedUrl()));
            imageViews.put(searchRecord.getImageView(), searchRecord.getFeedUrl());

            if(imageViewReused(searchRecord))
                return;
            Bitmap bmp=getBitmap(searchRecord.getImageURL());
            memoryCache.put(searchRecord.getFeedUrl(), bmp);
 //           if(imageViewReused(searchRecord))
 //               return;
            BitmapDisplayer bd=new BitmapDisplayer(bmp, searchRecord);
            Activity a=(Activity)searchRecord.getImageView().getContext();
            a.runOnUiThread(bd);
        }

		private String getImageUrlFromRssUrl(String rssUrl) {
			
			String imageUrl=null;
			File dir=FileUtility.getFeedDirectory();
			String fileDir=dir.getAbsolutePath();
			String filePath = fileDir + "/"+ FileUtility.getTimeStampedFileName();
			RSSParser rp=new RSSParser(rssUrl,filePath);
			rp.downloadRSS();
			rp.parseRss();
			imageUrl=rp.getSubscriptionImageUrl();
			
			return imageUrl;
		}
    }
    
    boolean imageViewReused(SearchRecord sr){
        String tag=imageViews.get(sr.getImageView());

        if(tag==null || !tag.equals(sr.getFeedUrl()))
        {
            return true;
        }
        return false;
    }
    
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        SearchRecord searchRecord;
        public BitmapDisplayer(Bitmap b, SearchRecord sr){bitmap=b;searchRecord=sr;}
        public void run()
        {
            if(imageViewReused(searchRecord))
                return;
            if(bitmap!=null)
                searchRecord.getImageView().setImageBitmap(bitmap);
            else
                searchRecord.getImageView().setImageBitmap(null);
        }
    }
 
    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }
    
    public class MemoryCache {
        private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());
     
        public Bitmap get(String id){
            if(!cache.containsKey(id))
                return null;
            SoftReference<Bitmap> ref=cache.get(id);
            return ref.get();
        }
     
        public void put(String id, Bitmap bitmap){
            cache.put(id, new SoftReference<Bitmap>(bitmap));
        }
     
        public void clear() {
            cache.clear();
        }
    }
    
    public class FileCache {
     
        private File cacheDir;
     
        public FileCache(Context context){
            //Find the dir to save cached images
            if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
                cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
            else
                cacheDir=context.getCacheDir();
            if(!cacheDir.exists())
                cacheDir.mkdirs();
        }
     
        public File getFile(String url){
            //I identify images by hashcode. Not a perfect solution, good for the demo.
            String filename=String.valueOf(url.hashCode());
            //Another possible solution (thanks to grantland)
            //String filename = URLEncoder.encode(url);
            File f = new File(cacheDir, filename);
            return f;
     
        }
     
        public void clear(){
            File[] files=cacheDir.listFiles();
            if(files==null)
                return;
            for(File f:files)
                f.delete();
        }
     
    }
 }
